package com.bolt.support.spring.jpa.jpql;

import com.bolt.common.utils.DateUtil;
import com.bolt.common.utils.StrUtil;
import com.bolt.support.query.conditions.domain.Compare;
import com.bolt.support.query.conditions.domain.Nested;
import com.bolt.support.query.conditions.domain.SqlClause;

import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.function.Consumer;

public class CompareClause implements Compare<CompareClause, SqlClause>, Nested<CompareClause>, SqlClause<OperationClause> {

    protected final CompareClause typedThis = (CompareClause) this;

    private OperationClause opMixin;


    public OperationClause getOpMixin() {
        return opMixin;
    }

    public CompareClause() {
    }

    public CompareClause(Ops ops, SqlClause property, Object matchValue) {
        opMixin = Clauses.addCompare(ops, opMixin, property, Clauses.of(property, matchValue));
    }

    /**
     * 等于 =
     *
     * @param property   字段
     * @param matchValue 值
     * @return
     */
    @Override
    public CompareClause eq(SqlClause property, Object matchValue) {
        if (matchValue instanceof Date) {
            Date dateTime = (Date) matchValue;
            Date start = DateUtil.parseDate(DateUtil.formatDate(dateTime, DateUtil.DEFAULT_DATE_FORMAT) + " 00:00:00");
            Date end = DateUtil.parseDate(DateUtil.formatDate(dateTime, DateUtil.DEFAULT_DATE_FORMAT) + " 23:59:59");
            this.between(property, start, end);
        } else {
            opMixin = Clauses.addCompare(Ops.EQ, opMixin, property, Clauses.of(property, matchValue));
        }
        return typedThis;
    }

    @Override
    public CompareClause eq(SqlClause left, SqlClause right) {
        opMixin = Clauses.addCompare(Ops.EQ, opMixin, left, right);
        return typedThis;
    }

    /**
     * 不等于 &lt;&gt;
     *
     * @param property   字段
     * @param matchValue 值
     * @return
     */
    @Override
    public CompareClause ne(SqlClause property, Object matchValue) {
        opMixin = Clauses.addCompare(Ops.NE, opMixin, property, Clauses.of(property, matchValue));
        return typedThis;
    }

    @Override
    public CompareClause ne(SqlClause left, SqlClause right) {
        opMixin = Clauses.addCompare(Ops.NE, opMixin, left, right);
        return typedThis;
    }

    /**
     * 大于 &gt;
     *
     * @param property   字段
     * @param matchValue 值
     * @return
     */
    @Override
    public CompareClause gt(SqlClause property, Object matchValue) {
        return gt(property, Clauses.of(property, matchValue));
    }

    @Override
    public CompareClause gt(SqlClause left, SqlClause right) {
        opMixin = Clauses.addCompare(Ops.GT, opMixin, left, right);
        return typedThis;
    }

    /**
     * 大于等于 &gt;=
     *
     * @param property   字段
     * @param matchValue 值
     * @return
     */
    @Override
    public CompareClause ge(SqlClause property, Object matchValue) {
        return ge(property, Clauses.of(property, matchValue));
    }

    @Override
    public CompareClause ge(SqlClause left, SqlClause right) {
        opMixin = Clauses.addCompare(Ops.GE, opMixin, left, right);
        return typedThis;
    }

    /**
     * 小于 &lt;
     *
     * @param property   字段
     * @param matchValue 值
     * @return
     */
    @Override
    public CompareClause lt(SqlClause property, Object matchValue) {
        return lt(property, Clauses.of(property, matchValue));
    }

    @Override
    public CompareClause lt(SqlClause left, SqlClause right) {
        opMixin = Clauses.addCompare(Ops.LT, opMixin, left, right);
        return typedThis;
    }

    /**
     * 小于等于 &lt;=
     *
     * @param property
     * @param matchValue
     * @return
     */
    @Override
    public CompareClause le(SqlClause property, Object matchValue) {
        return le(property, Clauses.of(property, matchValue));
    }

    @Override
    public CompareClause le(SqlClause left, SqlClause right) {
        opMixin = Clauses.addCompare(Ops.LE, opMixin, left, right);
        return typedThis;
    }


    public CompareClause isBlank(SqlClause property) {

        return or(Clauses.addCompare(Ops.ISNULL, opMixin, property),
                Clauses.addCompare(Ops.EQ, opMixin, property, Clauses.of(property, StrUtil.EMPTY)));
    }

    public CompareClause notBlank(SqlClause property) {

        return and(Clauses.addCompare(Ops.NOTNULL, opMixin, property),
                Clauses.addCompare(Ops.NE, opMixin, property, Clauses.of(property, StrUtil.EMPTY)));
    }

    public CompareClause isNull(SqlClause clause) {
        opMixin = Clauses.addCompare(Ops.ISNULL, opMixin, clause);
        return typedThis;
    }

    public CompareClause notNull(SqlClause clause) {
        opMixin = Clauses.addCompare(Ops.NOTNULL, opMixin, clause);
        return typedThis;
    }

    public CompareClause and(CompareClause... clauses) {
        OperationClause[] operationClause = Arrays.stream(clauses).map(k -> k.getOpMixin()).toArray(OperationClause[]::new);
        opMixin = Clauses.addCompare(Ops.AND, opMixin, operationClause);
        return typedThis;
    }

    public CompareClause and(OperationClause... clauses) {
        opMixin = Clauses.addCompare(Ops.AND, opMixin, clauses);
        return typedThis;
    }

    public CompareClause or(CompareClause... clauses) {
        OperationClause[] operationClause = Arrays.stream(clauses).map(k -> k.getOpMixin()).toArray(OperationClause[]::new);
        opMixin = Clauses.addCompare(Ops.OR, opMixin, operationClause);
        return typedThis;
    }

    public CompareClause or(OperationClause... clauses) {
        opMixin = Clauses.addCompare(Ops.OR, opMixin, clauses);
        return typedThis;
    }

    @Override
    public CompareClause and(Consumer<CompareClause> consumer) {
        CompareClause instance = new CompareClause();
        consumer.accept(instance);
        opMixin = Clauses.addCompare(Ops.AND, opMixin, instance.getOpMixin());
        return typedThis;
    }

    @Override
    public CompareClause or(Consumer<CompareClause> consumer) {
        CompareClause instance = new CompareClause();
        consumer.accept(instance);
        opMixin = Clauses.addCompare(Ops.OR, opMixin, instance.getOpMixin());
        return typedThis;
    }

    /**
     * BETWEEN 值1 AND 值2
     *
     * @param property 字段
     * @param from     值1
     * @param to       值2
     * @return CompareClause
     */
    public CompareClause between(SqlClause property, Object from, Object to) {
        if (from == null) {
            if (to != null) {
                opMixin = Clauses.addCompare(Ops.LE, opMixin, property, Clauses.of(property, StrUtil.EMPTY));
            } else {
                throw new IllegalArgumentException("Either from or to needs to be non-null");
            }
        } else if (to == null) {
            opMixin = Clauses.addCompare(Ops.GE, opMixin, property, Clauses.of(property, from));
        } else {
            opMixin = Clauses.addCompare(Ops.BETWEEN, opMixin, property
                    , Clauses.of(Clauses.getParamName(property, "_from"), from)
                    , Clauses.of(Clauses.getParamName(property, "_to"), to));
        }
        return typedThis;
    }

    /**
     * NOT BETWEEN 值1 AND 值2
     *
     * @param property 字段
     * @param val1     值1
     * @param val2     值2
     * @return CompareClause
     */
    public CompareClause notBetween(SqlClause property, Object val1, Object val2) {
        opMixin = Clauses.addCompare(Ops.NOTBETWEEN, opMixin, property
                , Clauses.of(Clauses.getParamName(property, "_start"), val1)
                , Clauses.of(Clauses.getParamName(property, "_end"), val2));
        return typedThis;
    }

    /**
     * LIKE '%值%'
     *
     * @param property 字段
     * @param val      值
     * @return CompareClause
     */
    public CompareClause like(SqlClause property, String val) {
        opMixin = Clauses.addCompare(Ops.LIKE, opMixin, property, Clauses.of(property, "%" + val + "%"));
        return typedThis;
    }

    /**
     * NOT LIKE '%值%'
     *
     * @param property 字段
     * @param val      值
     * @return CompareClause
     */
    public CompareClause notLike(SqlClause property, String val) {
        opMixin = Clauses.addCompare(Ops.NOTLIKE, opMixin, property, Clauses.of(property, "%" + val + "%"));
        return typedThis;
    }

    /**
     * LIKE '%值'
     *
     * @param property 字段
     * @param val      值
     * @return CompareClause
     */
    public CompareClause likeLeft(SqlClause property, String val) {
        opMixin = Clauses.addCompare(Ops.LIKELEFT, opMixin, property, Clauses.of(property, "%" + val));
        return typedThis;
    }


    /**
     * LIKE '值%'
     *
     * @param property 字段
     * @param val      值
     * @return CompareClause
     */
    public CompareClause likeRight(SqlClause property, String val) {
        opMixin = Clauses.addCompare(Ops.LIKERIGHT, opMixin, property, Clauses.of(property, val + "%"));
        return typedThis;
    }

    public CompareClause in(SqlClause property, Collection<?> val) {
        opMixin = Clauses.addCompare(Ops.IN, opMixin, property, Clauses.of(property, val));
        return typedThis;
    }

    public CompareClause notIn(SqlClause property, Collection<?> val) {
        opMixin = Clauses.addCompare(Ops.NOTIN, opMixin, property, Clauses.of(property, val));
        return typedThis;
    }

    @Override
    public OperationClause render(boolean addAlias) {
        return opMixin;
    }


    @Override
    public CompareClause contains(SqlClause property, String val) {
        opMixin = Clauses.addCompare(Ops.CONTAINS, opMixin, property, Clauses.of(property, val));
        return typedThis;
    }
}

 